#include <stdlib.h>
#include <kernel.h>
#include <zephyr.h>
#include <version.h>
#include <shell/shell.h>


/* sample for static subset */

static int cmd_info(const struct shell *sh, size_t argc, char **argv)
{
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);

	shell_print(sh, CONFIG_BOARD);
    shell_print(sh, "Zephyr version %s", KERNEL_VERSION_STRING);
	return 0;
}

static int cmd_info_arg(const struct shell *sh, size_t argc, char **argv)
{
    if(argc == 1){
        shell_help(sh);
        return SHELL_CMD_HELP_PRINTED;
    }

	shell_print(sh, "Total argc %d:", argc);
	for(size_t i=0; i<argc; i++){
		shell_print(sh, "argv[%d]: %s", i, argv[i]);
	}

	if(!strcmp(argv[1], "board")){
		shell_print(sh, CONFIG_BOARD);
	}else if(!strcmp(argv[1], "version")){
		shell_print(sh, "Zephyr version %s", KERNEL_VERSION_STRING);
	}else{
		shell_help(sh);
        return 2;
	}

	return 0;
}

static int cmd_info_board(const struct shell *sh, size_t argc, char **argv)
{
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);

	uint8_t data[] = { 1, 2, 3, 4, 5, 6, 7, 8,
		9, 10, 11, 12, 13, 14, 15, 16,
		17, 18, 19, 20, 21, 22, 23, 24,
		25, 26, 27, 28, 29, 30, 31, 32,
		33, 34, };

	shell_print(sh, CONFIG_BOARD);
	shell_info(sh, CONFIG_BOARD);
	shell_warn(sh, CONFIG_BOARD);
	shell_error(sh, CONFIG_BOARD);

	shell_info(sh, "shell_hexdump_line");
	shell_hexdump_line(sh, 0x8000, data, sizeof(data));

	shell_info(sh, "shell_hexdump");
	shell_hexdump(sh, data, sizeof(data));
	return 0;
}

static int cmd_info_version(const struct shell *sh, size_t argc, char **argv)
{
	ARG_UNUSED(argc);
	ARG_UNUSED(argv);

	shell_print(sh, "Zephyr version %s", KERNEL_VERSION_STRING);

	return 0;
}

static int cmd_shell_help(const struct shell *sh, size_t argc, char **argv)
{
	shell_print(sh, "show help: %d", argc);
    if(argc == 1){
        shell_help(sh);
        return SHELL_CMD_HELP_PRINTED;
    }

	for(size_t i=0; i< argc; i++){
		shell_print(sh, "check arg %d: %s", i, argv[i]);
	}

	return 0;
}


SHELL_STATIC_SUBCMD_SET_CREATE(subinfo,
	SHELL_CMD(board, NULL, "Show board command.", cmd_info_board),
    SHELL_CMD(version, NULL, "Show info command.", cmd_info_version),
	SHELL_SUBCMD_SET_END /* Array terminated. */
);

SHELL_STATIC_SUBCMD_SET_CREATE(shell_sample,
	SHELL_CMD(info, NULL, "Show info command.", cmd_info),
    SHELL_CMD(subinfo, &subinfo, "Show info command by sub.", NULL),
    SHELL_CMD_ARG(arginfo, NULL, "<board version>", cmd_info_arg, 0, 1),
	SHELL_SUBCMD_SET_END /* Array terminated. */
);

SHELL_CMD_REGISTER(shell_sample, &shell_sample, "Sample commands", cmd_shell_help);
SHELL_CMD_REGISTER(shell_sample_sub, &shell_sample, "Sample commands only subcmd", NULL);
SHELL_CMD_REGISTER(shell_sample_handler, NULL, "Sample commands only handler", cmd_shell_help);
SHELL_CMD_REGISTER(shell_sample_null, NULL, "Sample NULL commands", NULL);
SHELL_CMD_ARG_REGISTER(shell_sample_args, &shell_sample, "Sample arg commands with handle", cmd_shell_help, 3, 4);


/* sample for dynamic subset */

static int cmd_dynamic_total(const struct shell *sh, size_t argc, char **argv);
static int cmd_dynamic_org_name(const struct shell *sh, size_t argc, char **argv);
static int cmd_dynamic_new_name(const struct shell *sh, size_t argc, char **argv);

/* 动态命令数组 */
static struct shell_static_entry dynamic_entrys[]=
{
	/* 改变动态命令总数 */
	{
		.syntax = "total",
		.handler = cmd_dynamic_total,
		.subcmd = NULL,
		.help = "Set total cmd number, must more than 1.",
		.args.mandatory = 0,
		.args.optional = 0,
	},

	/* 使用默认子命令名 */
	{
		.syntax = "org_name",
		.handler = cmd_dynamic_org_name,
		.subcmd = NULL,
		.help = "Change to org cmd name.",
		.args.mandatory = 0,
		.args.optional = 0,
	},

	/* 使用新的子命令名 */
	{
		.syntax = "new_name",
		.handler = cmd_dynamic_new_name,
		.subcmd = NULL,
		.help = "Change to new cmd name.",
		.args.mandatory = 0,
		.args.optional = 0,
	},

	/* 动态子命令的子命令演示 */
	{
		.syntax = "subcmd",
		.handler = NULL,
		.subcmd = &shell_sample,
		.help = "Show dynamic sub cmd.",
		.args.mandatory = 0,
		.args.optional = 0,
	},
	{
		.syntax = "cmd1",
		.handler = NULL,
		.subcmd = NULL,
		.help = "Show dynamic command cmd1.",
		.args.mandatory = 0,
		.args.optional = 0,
	},
	{
		.syntax = "cmd2",
		.handler = NULL,
		.subcmd = NULL,
		.help = "Show dynamic command cmd2.",
		.args.mandatory = 0,
		.args.optional = 0,
	}
};

/* 动态命令修改命令符号的新符号字符串 */
char * change_name[] = {
	"total_new",
	"org_name_new",
	"new_name_new",
	"cmd1_new",
	"cmd2_new",
	"cmd3_new",
};

/* 动态命令可用数量 */
static uint32_t dynamic_cmd_num = sizeof(dynamic_entrys)/sizeof(struct shell_static_entry);

/* 新旧命令符号 */
static uint8_t cmd_name_flag = 0;

static int cmd_dynamic_total(const struct shell *sh, size_t argc, char **argv)
{
    if(argc < 2){
        shell_help(sh);
        return SHELL_CMD_HELP_PRINTED;
    }

	uint32_t num = (uint32_t)atoi(argv[1]);
	if(num<1 && num > sizeof(dynamic_entrys)/sizeof(struct shell_static_entry)){
		shell_error(sh, "total set fail, must in 1~%d", sizeof(dynamic_entrys)/sizeof(struct shell_static_entry));
		return -ENOEXEC;
	}

	/* 改变动态命令的数量 */
	shell_print(sh, "set total cmd num %d", num);
	dynamic_cmd_num = num;

	return 0;
}

static int cmd_dynamic_org_name(const struct shell *sh, size_t argc, char **argv)
{
	cmd_name_flag = 0;
	return 0;
}

static int cmd_dynamic_new_name(const struct shell *sh, size_t argc, char **argv)
{
	cmd_name_flag = 1;
	return 0;
}

static void dynamic_cmd_get(size_t idx, struct shell_static_entry *entry)
{
	if(idx<dynamic_cmd_num){
		entry->syntax = cmd_name_flag? change_name[idx]:dynamic_entrys[idx].syntax;
		entry->handler  = dynamic_entrys[idx].handler;
		entry->subcmd = dynamic_entrys[idx].subcmd;
		entry->help = dynamic_entrys[idx].help;
		entry->args.mandatory = dynamic_entrys[idx].args.mandatory;
		entry->args.optional = dynamic_entrys[idx].args.optional;
	}else{
		entry->syntax = NULL;
	}
}

SHELL_DYNAMIC_CMD_CREATE(dynamic_set, dynamic_cmd_get);
SHELL_CMD_REGISTER(shell_dynamic, &dynamic_set,
		   "Sample dynamic command usage.", NULL);


/* 字典子命令演示 */
static int dict_cmd_handler(const struct shell *shell,
                            size_t argc, char **argv, void *data)
{
        int action_data;

        /* 数据转换 */
        action_data = (int)data;

		/* 这里action数据做处理, 例如设置波特率 */


        shell_print(shell, "action: %s\n"
                           "action data: %d",
                           argv[0],
                           action_data);

        return 0;
}

/* 注册了4个字典命令 */
SHELL_SUBCMD_DICT_SET_CREATE(subdict, dict_cmd_handler,
        (baud_4800, 1), (baud_9600, 2), (baud_38400, 3), (baud_115200, 4)
);
SHELL_CMD_REGISTER(shell_dict, &subdict, "Sample dict command usage.", NULL);





SHELL_STATIC_SUBCMD_SET_CREATE(shell_wildcard,
	SHELL_CMD(cmd11, NULL, "Show info command.", NULL),
    SHELL_CMD(cmd12, &subinfo, "Show info command by sub.", NULL),
    SHELL_CMD(cmd3, NULL, "<board version>", NULL),
	SHELL_SUBCMD_SET_END /* Array terminated. */
);

static int wildcard_cmd_handler(const struct shell *shell,
                            size_t argc, char **argv, void *data)
{
	shell_print(shell, "Total argc %d:", argc);
	for(size_t i=0; i<argc; i++){
		shell_print(shell, "argv[%d]: %s", i, argv[i]);
	}

	return 0;
}

SHELL_CMD_ARG_REGISTER(shell_wildcard, &shell_wildcard, "Sample wildcard", wildcard_cmd_handler, 2, 255);






static int cmd_logout(const struct shell *shell,
                            size_t argc, char **argv, void *data);
static int cmd_admin_cmd(const struct shell *shell,
                            size_t argc, char **argv, void *data);

static struct shell_static_entry admin_entrys[]=
{
	{
		.syntax = "chmod",
		.handler = cmd_admin_cmd,
		.subcmd = NULL,
		.help = "change work mode.",
		.args.mandatory = 0,
		.args.optional = 0,
	},

	{
		.syntax = "passwd",
		.handler = cmd_admin_cmd,
		.subcmd = NULL,
		.help = "Change password.",
		.args.mandatory = 0,
		.args.optional = 0,
	},

	{
		.syntax = "delete",
		.handler = cmd_admin_cmd,
		.subcmd = NULL,
		.help = "Delect all data.",
		.args.mandatory = 0,
		.args.optional = 0,
	},
	{
		.syntax = "logout",
		.handler = cmd_logout,
		.subcmd = NULL,
		.help = "logout admin.",
		.args.mandatory = 0,
		.args.optional = 0,
	},
	{
		.syntax = NULL,
	}
};

static uint8_t admin_login_flag = 0;

/* 动态命令，作为登录命令的子命令，在登录后才会有子命令 */
static void admin_cmd_get(size_t idx, struct shell_static_entry *entry)
{
	if(admin_login_flag){
		entry->syntax = admin_entrys[idx].syntax;
		entry->handler  = admin_entrys[idx].handler;
		entry->subcmd = admin_entrys[idx].subcmd;
		entry->help = admin_entrys[idx].help;
		entry->args.mandatory = admin_entrys[idx].args.mandatory;
		entry->args.optional = admin_entrys[idx].args.optional;
	}else{
		entry->syntax = NULL;
	}
}

/* admin登录命令 */
static int cmd_admin(const struct shell *shell,
                            size_t argc, char **argv, void *data)
{
	/* root指向login命令 */
	shell_set_root_cmd("login");

	/* 指向login后要求输入密码，对输入进行隐藏 */
	shell_obscure_set(shell, true);

	/* 改变登录时的提示符号 */
	shell_prompt_change(shell, "login: ");
}

/* 登录命令 */
static int cmd_login(const struct shell *shell,
                            size_t argc, char **argv, void *data)
{
	static uint32_t attempts;
	/* 输入exit退出登录 */
	if (strcmp(argv[1], "exit") == 0) {
		shell_set_root_cmd(NULL);
		shell_obscure_set(shell, false);
		shell_prompt_change(shell, "uart:~$ ");
		shell_print(shell, "\n");
		admin_login_flag = 0;
		return 0;
	}

	/* 匹配登录密码zgp */
	if (strcmp(argv[1], "zgp") != 0) {
		shell_error(shell, "Incorrect password!");
		attempts++;
		if (attempts > 3) {
			k_sleep(K_SECONDS(attempts));
		}
		return -EINVAL;
	}

	/* 清除历史记录，避免密码被历史记录调用出来 */
	z_shell_history_purge(shell->history);
	/* 登录成功，关掉隐藏输入 */
	shell_obscure_set(shell, false);

	/* 进入到admin界面 */
	shell_set_root_cmd("admin");
	shell_prompt_change(shell, "admin:~$ ");
	shell_print(shell, "Shell Login Admin\n");
	shell_print(shell, "Hit tab for help.\n");
	admin_login_flag = 1;
	attempts = 0;
	return 0;
}

/* 从admin登出 */
static int cmd_logout(const struct shell *shell,
                            size_t argc, char **argv, void *data)
{
	shell_set_root_cmd(NULL);
	shell_obscure_set(shell, false);
	shell_prompt_change(shell, "uart:~$ ");
	shell_print(shell, "\n");
	admin_login_flag = 0;
	return 0;
}

static int cmd_admin_cmd(const struct shell *shell,
                            size_t argc, char **argv, void *data)
{
	shell_print(shell, "%s", argv[0]);
	return 0;
}

SHELL_DYNAMIC_CMD_CREATE(admin_set, admin_cmd_get);
SHELL_COND_CMD_REGISTER(CONFIG_SHELL_START_OBSCURED, admin, &admin_set, "login admin.", cmd_admin);
SHELL_COND_CMD_ARG_REGISTER(CONFIG_SHELL_START_OBSCURED, login, NULL, "<Password>", cmd_login, 2, 0);